<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue + Cesium WebSocket实时轨迹播放</title>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <script src="/Cesium/Cesium.js"></script>
    <link href="/Cesium/Widgets/widgets.css" rel="stylesheet">
    <style>
        body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
        #app { width: 100%; height: 100vh; }
        #cesiumContainer { width: 100%; height: 100vh; }
        .info {
            position: absolute; top: 10px; left: 10px; z-index: 1000;
            background: rgba(0,0,0,0.8); color: #fff; padding: 14px; border-radius: 6px; max-width: 520px;
        }
        .row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 8px; }
        .row button { background: #4CAF50; color: #fff; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; }
        .row button:hover { background: #45a049; }
        .row button.danger { background: #e84c3d; }
        .row button.danger:hover { background: #cf3e30; }
        .row button:disabled { background: #666; cursor: not-allowed; }
        .row select, .row input { padding: 4px 6px; font-size: 12px; }
        .kv { font-size: 12px; line-height: 1.6; }
        .kv b { color: #ffd666; }
        .timeline { display:flex; align-items:center; gap:8px; margin-top:6px; }
        .timeline input[type="range"] { width: 240px; }
        
        /* 右侧表格样式 */
        .geojson-table {
            position: absolute; top: 10px; right: 10px; z-index: 1000;
            background: rgba(0,0,0,0.9); color: #fff; padding: 14px; border-radius: 6px;
            width: 400px; max-height: 80vh; overflow-y: auto;
        }
        .geojson-table h3 { margin: 0 0 12px 0; color: #ffd666; font-size: 14px; }
        .geojson-table table { width: 100%; border-collapse: collapse; font-size: 11px; }
        .geojson-table th, .geojson-table td { 
            padding: 6px 8px; text-align: left; border-bottom: 1px solid #333; 
        }
        .geojson-table th { background: rgba(255,255,255,0.1); font-weight: bold; }
        .geojson-table tr:hover { background: rgba(255,255,255,0.05); }
        .geojson-table .actions { display: flex; gap: 4px; }
        .geojson-table .actions button { 
            padding: 2px 6px; font-size: 10px; background: #2196F3; 
            border: none; color: white; border-radius: 3px; cursor: pointer; 
        }
        .geojson-table .actions button:hover { background: #1976D2; }
        .geojson-table .actions button.danger { background: #f44336; }
        .geojson-table .actions button.danger:hover { background: #d32f2f; }
        .geojson-table .json-preview { 
            max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; 
        }
    </style>
</head>
<body>
<div id="app">
    <div id="cesiumContainer"></div>
            <div class="info">
        <div class="kv">状态：<b>{{ status }}</b></div>
                 <div class="kv">WebSocket：<b :style="{color: isConnected ? '#4CAF50' : '#f44336'}">{{ isConnected ? '已连接' : '未连接' }}</b></div>
         <div class="kv">轨迹数量：<b>{{ flightTracks.length }}</b></div>
         <div class="kv">初始坐标：<b>{{ initialLongitude.toFixed(6) }}, {{ initialLatitude.toFixed(6) }}</b></div>
                 <div class="row">
             <label>选择轨迹：
                                   <select v-model.number="currentTrackIndex" @change="setCurrentTrack(currentTrackIndex)">
                      <option v-for="(track, index) in flightTracks" :key="track.id" :value="index">
                          {{ track.aircraft_type || '轨迹' }}{{ index + 1 }} ({{ track.mission_type || track.id.substring(0, 8) }})
                      </option>
                  </select>
             </label>
             <label>WebSocket地址：
                 <input v-model="websocketUrl" style="width: 200px;" placeholder="ws://127.0.0.1:6088/uam/v2/ws/uam/channel/245">
                 <button @click="reconnectWebSocket" :disabled="isConnected">重连</button>
             </label>
         </div>
                                   <div class="row">
                          <button @click="focusFirstPoint">定位到起点</button>
              <button @click="focusToInitialLocation">定位到初始位置</button>
              <button @click="resetToInitialLocation">重置位置</button>
              <button @click="forceUpdateZoomLevel">更新缩放级别</button>
              <button @click="clearAllTracks" class="danger">清除所有轨迹</button>
              <button @click="clearHistoricalTracks" class="danger">清除历史轨迹</button>
                         <label>渲染间隔：
                 <select v-model.number="renderDelay" @change="updateRenderDelay">
                     <option :value="500">0.5秒</option>
                     <option :value="1000">1秒</option>
                     <option :value="2000">2秒</option>
                     <option :value="5000">5秒</option>
                 </select>
             </label>
             <label>更新节流：
                 <select v-model.number="updateThrottle" @change="updateRenderDelay">
                     <option :value="200">0.2秒</option>
                     <option :value="500">0.5秒</option>
                     <option :value="1000">1秒</option>
                 </select>
             </label>
             <label>实时更新：
                 <input type="checkbox" v-model="enableRealTimeUpdate" @change="toggleRealTimeUpdate">
             </label>
                           <label>历史轨迹：
                  <input type="checkbox" v-model="showHistoricalTracks" @change="toggleHistoricalTracks">
              </label>
              <label>分段颜色：
                  <input type="checkbox" v-model="enableSegmentedColors">
              </label>
        </div>
                 <div class="row">
             <button @click="startDrawPolygon" :disabled="drawing">开始绘制多边形</button>
             <button @click="finishDrawPolygon" :disabled="!drawing">完成</button>
             <button @click="clearDrawPolygon" class="danger">清空多边形</button>
                           <span style="font-size:12px;color:#ccc">提示：左键落点、移动预览、右键完成。地图滚动后不会自动重新定位。</span>
         </div>
         
         <!-- 时间轴控制 -->
         <div class="row">
             <button @click="playPause" :disabled="!hasValidTrack">{{ playing ? '暂停' : '播放' }}</button>
             <button @click="stepForward" :disabled="!hasValidTrack">向前</button>
             <button @click="stepBackward" :disabled="!hasValidTrack">向后</button>
             <button @click="resetTime" :disabled="!hasValidTrack">重置</button>
             <label>播放速度：
                 <select v-model.number="playbackSpeed" @change="updatePlaybackSpeed">
                     <option :value="0.5">0.5x</option>
                     <option :value="1">1x</option>
                     <option :value="2">2x</option>
                     <option :value="5">5x</option>
                     <option :value="10">10x</option>
                 </select>
             </label>
         </div>
         
         <!-- 时间轴滑块 -->
         <div class="timeline" v-if="hasValidTrack">
             <span style="font-size:12px;">时间轴：</span>
             <input type="range" 
                    v-model.number="currentTimePercent" 
                    @input="seekToTime"
                    min="0" 
                    max="100" 
                    step="0.1"
                    style="width: 200px;">
             <span style="font-size:12px;">{{ formatTime(currentTime) }} / {{ formatTime(totalDuration) }}</span>
         </div>

                 <div class="kv">当前位置：<b>{{ posText }}</b></div>
         <div class="kv">当前区段：<b>{{ segText }}</b></div>
         <div class="kv">地图缩放：<b>{{ currentZoomLevelDisplay }}级</b></div>
         <div class="kv">定位状态：<b :style="{color: initialLocationSet ? '#4CAF50' : '#ff9800'}">{{ initialLocationSet ? '已定位' : '定位中...' }}</b></div>
         <div class="kv">轨迹统计：<b>{{ trackStats }}</b></div>
        <div class="kv" v-if="polygonGeoJSON">
            <div style="margin-top: 8px;">
                <strong style="color: #ffd666;">多边形GeoJSON：</strong>
                <button @click="copyGeoJSON" style="margin-left: 8px; background: #2196F3; color: white; border: none; padding: 2px 6px; border-radius: 3px; font-size: 10px; cursor: pointer;">复制</button>
            </div>
            <pre style="background: rgba(0,0,0,0.3); padding: 8px; border-radius: 4px; font-size: 10px; max-height: 120px; overflow-y: auto; margin-top: 4px; white-space: pre-wrap; word-break: break-all;">{{ polygonGeoJSON }}</pre>
        </div>
    </div>
    
    <!-- 右侧GeoJSON记录表格 -->
    <div class="geojson-table">
        <h3>GeoJSON记录 ({{ geojsonRecords.length }})</h3>
        <table v-if="geojsonRecords.length > 0">
            <thead>
                <tr>
                    <th>序号</th>
                    <th>时间</th>
                    <th>点数</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="(record, index) in geojsonRecords" :key="index">
                    <td>{{ index + 1 }}</td>
                    <td>{{ formatTime(record.timestamp) }}</td>
                    <td>{{ record.pointCount }}</td>
                    <td class="actions">
                        <button @click="copyRecordGeoJSON(index)" title="复制GeoJSON">复制</button>
                        <button @click="viewRecordGeoJSON(index)" title="查看完整GeoJSON">查看</button>
                        <button @click="deleteRecord(index)" class="danger" title="删除记录">删除</button>
                    </td>
                </tr>
            </tbody>
        </table>
        <div v-else style="text-align: center; color: #999; padding: 20px;">
            暂无绘制记录
        </div>
    </div>
</div>

<script>
const { createApp } = Vue;
createApp({
    data() {
        return {
            status: '正在初始化...',
            viewer: null,
            entity: null,
            trackEntity: null,
            positionProp: null,
            start: null,
            end: null,
            playing: false,
            speed: 1,
            jumpSec: 0,
            currentSec: 0,
            endSeconds: 0,
            posText: '-',
            segText: '-',
            timeMode: 'relative-fixed',
            isSeeking: false,
            // WebSocket相关
            websocket: null,
            websocketUrl: 'ws://127.0.0.1:6088/uam/v2/ws/uam/channel/245', // WebSocket服务器地址
            isConnected: false,
            reconnectInterval: null,
            reconnectAttempts: 0,
            maxReconnectAttempts: 5,
            
            // 实时轨迹数据
            flightTracks: [], // 将从WebSocket获取
            trackColors: [Cesium.Color.RED, Cesium.Color.BLUE, Cesium.Color.GREEN, Cesium.Color.YELLOW],
            trackEntities: {}, // 存储轨迹实体 {trackId: entity}
            aircraftEntities: {}, // 存储飞机实体 {trackId: entity}
            trackPolylines: {}, // 存储轨迹线实体 {trackId: entity}
            
                         // 实时渲染相关
             realTimeMode: true, // 实时模式
             renderInterval: null,
             lastRenderTime: 0,
             renderDelay: 2000, // 每2秒渲染一次新点
             lastUpdateTime: 0, // 上次更新时间
             updateThrottle: 500, // 更新节流间隔（毫秒）
             enableRealTimeUpdate: true, // 是否启用实时更新
                          showHistoricalTracks: true, // 是否显示历史轨迹
             enableSegmentedColors: true, // 是否启用分段颜色（飞过的轨迹用青色）
             // 初始定位坐标
             initialLongitude: 120.16196180060605,
             initialLatitude: 30.14332020097707,
             initialZoomLevel: 13,
             currentZoomLevel: 13, // 当前地图缩放级别
             initialLocationSet: false, // 是否已经完成初始定位
             zoomUpdateTimeout: null, // 缩放级别更新防抖定时器
             // 当前选中的轨迹（用于播放）
             currentTrackIndex: 0,
             flightPath: [], // 将根据选中的轨迹动态设置
             
             // 时间轴控制相关
             playing: false, // 是否正在播放
             playbackSpeed: 1, // 播放速度
             currentTime: 0, // 当前时间（秒）
             totalDuration: 0, // 总时长（秒）
             currentTimePercent: 0, // 当前时间百分比
             playbackInterval: null, // 播放定时器
                         // 为每条轨迹创建独立的飞机实体
             aircraftEntities: [], // 存储每条轨迹的飞机实体
             // 绘制相关
             drawing: false,
             drawPositions: [], // Cartesian3
             drawPolygonEntity: null,
             drawOutlineEntity: null,
             drawHandler: null,
             polygonGeoJSON: null, // 存储多边形的GeoJSON
             geojsonRecords: [] // 存储所有GeoJSON记录
        };
    },
         computed: { 
         clock() { return this.viewer ? this.viewer.clock : null; },
         trackStats() {
             if (this.flightTracks.length === 0) return '暂无轨迹';
             
             const totalPoints = this.flightTracks.reduce((sum, track) => 
                 sum + (track.points ? track.points.length : 0), 0
             );
             
             const activeTracks = this.flightTracks.filter(track => {
                 if (!track.points || track.points.length === 0) return false;
                 const currentTime = Date.now();
                 const trackEndTime = track.startTime + track.points[track.points.length - 1].timestamp * 1000;
                 return currentTime <= trackEndTime + 60000; // 1分钟内的轨迹认为是活跃的
             }).length;
             
             return `${this.flightTracks.length}条轨迹，${totalPoints}个点，${activeTracks}条活跃`;
         },
                   hasValidTrack() {
              return this.flightTracks.length > 0 && 
                     this.currentTrackIndex >= 0 && 
                     this.currentTrackIndex < this.flightTracks.length &&
                     this.flightTracks[this.currentTrackIndex] &&
                     this.flightTracks[this.currentTrackIndex].points &&
                     this.flightTracks[this.currentTrackIndex].points.length > 0;
          },
          currentZoomLevelDisplay() {
              return this.currentZoomLevel;
          }
     },
    mounted() { 
        this.initCesium(); 
        this.connectWebSocket();
    },
                   beforeUnmount() {
          this.disconnectWebSocket();
          if (this.renderInterval) {
              clearInterval(this.renderInterval);
          }
          if (this.playbackInterval) {
              clearInterval(this.playbackInterval);
          }
          if (this.zoomUpdateTimeout) {
              clearTimeout(this.zoomUpdateTimeout);
          }
      },
    methods: {
        // 安全缩放到目标：兼容 Promise / 非 Promise 返回
        safeZoomTo(target) {
            try {
                const res = this.viewer.zoomTo(target);
                if (res && typeof res.then === 'function') {
                    res.catch(() => {
                        this.viewer.camera.setView({
                            destination: Cesium.Cartesian3.fromDegrees(116.4074, 39.9042, 2000000),
                            orientation: { heading: 0.0, pitch: -Cesium.Math.PI_OVER_TWO, roll: 0.0 }
                        });
                    });
                }
            } catch (_) {
                this.viewer.camera.setView({
                    destination: Cesium.Cartesian3.fromDegrees(116.4074, 39.9042, 2000000),
                    orientation: { heading: 0.0, pitch: -Cesium.Math.PI_OVER_TWO, roll: 0.0 }
                });
            }
        },
                 // 将相机定位到轨迹第一个点位置
         focusFirstPoint() {
             if (!this.flightPath || this.flightPath.length === 0) return;
             const p0 = this.flightPath[0];
             
             // 验证坐标的有效性
             if (p0 && 
                 typeof p0.longitude === 'number' && !isNaN(p0.longitude) &&
                 typeof p0.latitude === 'number' && !isNaN(p0.latitude) &&
                 p0.longitude >= -180 && p0.longitude <= 180 &&
                 p0.latitude >= -90 && p0.latitude <= 90) {
                 
                 const height = Math.max(30000, (p0.altitude || 0) + 80000);
                 try {
                     this.viewer.camera.setView({
                         destination: Cesium.Cartesian3.fromDegrees(p0.longitude, p0.latitude, height),
                         orientation: { heading: 0.0, pitch: -Cesium.Math.PI_OVER_TWO + 0.2, roll: 0.0 }
                     });
                     
                     // 更新状态
                     this.status = `已定位到轨迹起点: ${p0.longitude.toFixed(4)}, ${p0.latitude.toFixed(4)}`;
                 } catch (e) {
                     console.warn('定位到起点失败:', p0, e);
                     this.status = '定位到起点失败';
                 }
             }
         },
                 initCesium() {
             try {
                 this.viewer = new Cesium.Viewer('cesiumContainer', {
                     terrain: Cesium.Terrain.fromWorldTerrain(),
                     animation: true, timeline: true,
                     baseLayerPicker: true, geocoder: true, homeButton: true,
                     sceneModePicker: true, navigationHelpButton: true,
                     fullscreenButton: true, infoBox: true, selectionIndicator: true
                 });
                 this.viewer.scene.globe.depthTestAgainstTerrain = false;
                 
                 // 添加渲染错误处理
                 this.viewer.scene.renderError.addEventListener((scene, error) => {
                     console.error('Cesium渲染错误:', error);
                     this.status = '渲染错误，请检查数据';
                 });
                 
                 // 地图加载完成后定位到指定坐标
                 this.viewer.scene.globe.tileLoadProgressEvent.addEventListener((queuedTileCount) => {
                     if (queuedTileCount === 0 && !this.initialLocationSet) {
                         // 地图加载完成，定位到指定坐标
                         setTimeout(() => {
                             this.focusToInitialLocation();
                         }, 500);
                     }
                 });
                 
                 // 备用方案：延迟定位，确保地图完全加载
                 setTimeout(() => {
                     if (!this.initialLocationSet) {
                         this.focusToInitialLocation();
                     }
                 }, 3000);
                 
                 // 初始化实时渲染
                 this.initRealTimeRendering();
                 
                 this.clock.onTick.addEventListener(this.onTick);
                 
                 // 监听相机变化，更新缩放级别显示
                 this.viewer.camera.changed.addEventListener(() => {
                     // 只有在初始定位完成后才更新缩放级别
                     if (this.initialLocationSet) {
                         // 防抖处理，避免频繁更新
                         if (this.zoomUpdateTimeout) {
                             clearTimeout(this.zoomUpdateTimeout);
                         }
                         this.zoomUpdateTimeout = setTimeout(() => {
                             this.updateCurrentZoomLevel();
                         }, 100);
                     }
                 });
                 
                 // 标记是否已经完成初始定位
                 this.initialLocationSet = false;
                 
                 this.status = '地图初始化完成，正在定位到指定坐标...';
             } catch (e) { this.status = '初始化失败'; console.error(e); }
         },
         
         // 定位到初始位置
         focusToInitialLocation() {
             try {
                 const longitude = this.initialLongitude;
                 const latitude = this.initialLatitude;
                 const zoomLevel = this.initialZoomLevel;
                 
                 // 计算13级地图对应的相机高度
                 const height = this.calculateHeightForZoomLevel(zoomLevel);
                 
                 this.viewer.camera.setView({
                     destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, height),
                     orientation: { 
                         heading: 0.0, 
                         pitch: -Cesium.Math.PI_OVER_TWO + 0.1, 
                         roll: 0.0 
                     }
                 });
                 
                 console.log(`地图已定位到: ${longitude}, ${latitude}, 缩放级别: ${zoomLevel}`);
                 this.status = `地图已定位到指定坐标 (${longitude.toFixed(6)}, ${latitude.toFixed(6)})，缩放级别: ${zoomLevel}`;
                 
                 // 更新当前缩放级别
                 this.currentZoomLevel = zoomLevel;
                 
                 // 标记初始定位已完成
                 this.initialLocationSet = true;
             } catch (e) {
                 console.warn('定位到初始位置失败:', e);
                 this.status = '定位到初始位置失败';
             }
         },
         
         // 根据缩放级别计算相机高度
         calculateHeightForZoomLevel(zoomLevel) {
             // 13级地图对应的相机高度大约为 8000 米
             // 使用更精确的计算方法
             const earthRadius = 6371000; // 地球半径（米）
             const tileSize = 256; // 瓦片大小
             const pixelsPerTile = tileSize * Math.pow(2, zoomLevel);
             const metersPerPixel = (2 * Math.PI * earthRadius) / pixelsPerTile;
             const height = metersPerPixel * 1000; // 1000像素对应的高度
             
             return Math.max(height, 5000); // 最小高度5000米
         },
         
         // 更新当前缩放级别
         updateCurrentZoomLevel() {
             if (!this.viewer || !this.initialLocationSet) return;
             
             try {
                 const camera = this.viewer.camera;
                 const height = camera.positionCartographic.height;
                 
                 // 简化的缩放级别计算方法
                 // 13级对应约8000米高度，每级高度减半
                 const baseHeight = 8000;
                 const baseZoom = 13;
                 const zoomLevel = baseZoom - Math.log2(height / baseHeight);
                 
                 const newZoomLevel = Math.max(0, Math.round(zoomLevel));
                 
                 // 只有当缩放级别真正改变时才更新
                 if (newZoomLevel !== this.currentZoomLevel) {
                     this.currentZoomLevel = newZoomLevel;
                 }
             } catch (e) {
                 console.warn('更新缩放级别失败:', e);
             }
         },
         
         // 重置到初始位置
         resetToInitialLocation() {
             // 重置初始定位标记，允许重新定位
             this.initialLocationSet = false;
             this.focusToInitialLocation();
         },
         
         // 手动更新缩放级别
         forceUpdateZoomLevel() {
             this.updateCurrentZoomLevel();
         },
        
        // ================= WebSocket相关 =================
        connectWebSocket() {
            try {
                this.websocket = new WebSocket(this.websocketUrl);
                
                this.websocket.onopen = () => {
                    this.isConnected = true;
                    this.reconnectAttempts = 0;
                    this.status = 'WebSocket已连接，等待数据...';
                    console.log('WebSocket连接成功');
                };
                
                this.websocket.onmessage = (event) => {
                    this.handleWebSocketMessage(event.data);
                };
                
                this.websocket.onclose = () => {
                    this.isConnected = false;
                    this.status = 'WebSocket连接断开，尝试重连...';
                    console.log('WebSocket连接断开');
                    this.scheduleReconnect();
                };
                
                this.websocket.onerror = (error) => {
                    console.error('WebSocket错误:', error);
                    this.status = 'WebSocket连接错误';
                };
                
            } catch (error) {
                console.error('WebSocket连接失败:', error);
                this.status = 'WebSocket连接失败';
                this.scheduleReconnect();
            }
        },
        
        disconnectWebSocket() {
            if (this.websocket) {
                this.websocket.close();
                this.websocket = null;
            }
            if (this.reconnectInterval) {
                clearInterval(this.reconnectInterval);
                this.reconnectInterval = null;
            }
        },
        
        reconnectWebSocket() {
            this.disconnectWebSocket();
            this.reconnectAttempts = 0;
            this.connectWebSocket();
        },
        
        scheduleReconnect() {
            if (this.reconnectAttempts >= this.maxReconnectAttempts) {
                this.status = 'WebSocket重连失败，请检查服务器状态';
                return;
            }
            
            this.reconnectAttempts++;
            const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 10000); // 指数退避
            
            this.reconnectInterval = setTimeout(() => {
                this.status = `尝试重连WebSocket (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`;
                this.connectWebSocket();
            }, delay);
        },
        
        handleWebSocketMessage(data) {
            try {
                const message = JSON.parse(data);
                
                // 处理新的数据格式：msgType = 1 表示实时轨迹数据
                if (message.msgType === 1 && message.data && Array.isArray(message.data)) {
                    this.processTrackData(message.data);
                } else if (message.code === 200 && message.data && message.data.list) {
                    // 兼容旧格式
                    this.processTrackData(message.data.list);
                } else {
                    console.log('收到WebSocket消息:', message);
                }
            } catch (error) {
                console.error('解析WebSocket消息失败:', error);
            }
        },
        
        processTrackData(trackList) {
            // 处理轨迹数据
            trackList.forEach((trackData, index) => {
                const trackId = trackData.tid;
                const color = this.trackColors[index % this.trackColors.length];
                
                // 转换数据格式 - 兼容新旧格式，并过滤无效数据
                const points = trackData.pts
                    .filter(pt => pt && 
                        typeof pt.lng === 'number' && !isNaN(pt.lng) &&
                        typeof pt.lat === 'number' && !isNaN(pt.lat) &&
                        typeof pt.alt === 'number' && !isNaN(pt.alt) &&
                        pt.lng >= -180 && pt.lng <= 180 &&
                        pt.lat >= -90 && pt.lat <= 90)
                    .map((pt, ptIndex) => ({
                        longitude: pt.lng,
                        latitude: pt.lat,
                        altitude: pt.alt,
                        speed: pt.spd,
                        timestamp: pt.ts - trackData.startTime, // 相对时间戳
                        name: `轨迹${index + 1}点${ptIndex}`,
                        // 新增字段
                        battery: pt.batt,
                        timestamp_absolute: pt.ts
                    }));
                
                // 如果过滤后没有有效点，跳过这条轨迹
                if (points.length === 0) {
                    console.warn(`轨迹 ${trackId} 没有有效的坐标点，已跳过`);
                    return;
                }
                
                // 更新或创建轨迹
                this.updateTrack(trackId, {
                    id: trackId,
                    color: color,
                    points: points,
                    startTime: trackData.startTime,
                    endTime: trackData.endTime,
                    duration: trackData.dur,
                    distance: trackData.dist,
                    // 新增字段
                    aircraft_type: trackData.aircraft_type,
                    mission_type: trackData.mission_type,
                    esId: trackData.esId,
                    pid: trackData.pid,
                    relId: trackData.relId,
                    schema_requirement_id: trackData.schema_requirement_id,
                    sid: trackData.sid,
                    spId: trackData.spId,
                    typeId: trackData.typeId,
                    vid: trackData.vid
                });
            });
            
            const aircraftTypes = [...new Set(trackList.map(t => t.aircraft_type).filter(Boolean))];
            const typeInfo = aircraftTypes.length > 0 ? ` (${aircraftTypes.join(', ')})` : '';
            const totalPoints = this.flightTracks.reduce((sum, track) => 
                sum + (track.points ? track.points.length : 0), 0
            );
            this.status = `已接收${trackList.length}条轨迹数据${typeInfo}，累计${totalPoints}个轨迹点，实时渲染中...`;
        },
        
        updateTrack(trackId, trackData) {
            // 如果轨迹不存在，创建新的
            if (!this.flightTracks.find(t => t.id === trackId)) {
                this.flightTracks.push(trackData);
                
                // 创建轨迹线
                this.createTrackPolyline(trackId, trackData);
                
                // 创建飞机实体
                this.createAircraftEntity(trackId, trackData);
                
                // 如果是第一条轨迹，设置相机位置
                if (this.flightTracks.length === 1) {
                    this.focusFirstPoint();
                }
            } else {
                // 更新现有轨迹 - 累积轨迹点而不是替换
                const trackIndex = this.flightTracks.findIndex(t => t.id === trackId);
                const existingTrack = this.flightTracks[trackIndex];
                
                // 合并轨迹点，避免重复
                const existingPoints = existingTrack.points || [];
                const newPoints = trackData.points || [];
                
                // 创建时间戳映射，用于去重
                const existingTimestamps = new Set(existingPoints.map(p => p.timestamp_absolute));
                
                // 添加新的轨迹点（不重复的）
                const uniqueNewPoints = newPoints.filter(point => 
                    !existingTimestamps.has(point.timestamp_absolute)
                );
                
                if (uniqueNewPoints.length > 0) {
                    // 合并轨迹点并按时间排序
                    const allPoints = [...existingPoints, ...uniqueNewPoints].sort((a, b) => 
                        a.timestamp_absolute - b.timestamp_absolute
                    );
                    
                    // 更新轨迹数据
                    const updatedTrackData = {
                        ...trackData,
                        points: allPoints
                    };
                    
                    this.flightTracks[trackIndex] = updatedTrackData;
                    
                    // 更新轨迹线
                    this.updateTrackPolyline(trackId, updatedTrackData);
                    
                    // 更新飞机位置
                    this.updateAircraftPosition(trackId, updatedTrackData);
                    
                    console.log(`轨迹 ${trackId} 新增 ${uniqueNewPoints.length} 个轨迹点，总计 ${allPoints.length} 个点`);
                }
            }
        },
        
        // ================= 实时渲染相关 =================
        initRealTimeRendering() {
            // 启动实时渲染定时器（如果启用实时更新）
            if (this.enableRealTimeUpdate) {
                this.renderInterval = setInterval(() => {
                    this.renderNewPoints();
                }, this.renderDelay);
            }
        },
        
                 renderNewPoints() {
             const currentTime = Date.now();
             
             // 节流控制，避免过于频繁的更新
             if (currentTime - this.lastUpdateTime < this.updateThrottle) {
                 return;
             }
             
             // 批量更新所有轨迹和飞机位置，减少渲染次数
             this.flightTracks.forEach(track => {
                 if (track.points && track.points.length > 0) {
                     // 更新轨迹线（包括颜色）
                     this.updateTrackPolylinePositions(track.id, track);
                     
                     // 更新飞机位置
                     this.updateAircraftPosition(track.id, track);
                 }
             });
             
             this.lastRenderTime = currentTime;
             this.lastUpdateTime = currentTime;
         },
        
                 createTrackPolyline(trackId, trackData) {
             // 创建轨迹线实体 - 使用静态位置数组，避免频繁的CallbackProperty调用
             const polylineEntity = this.viewer.entities.add({
                 polyline: {
                     positions: [], // 初始为空数组
                     width: 3,
                     material: trackData.color.withAlpha(0.8),
                     clampToGround: false,
                     show: this.showHistoricalTracks // 根据设置控制可见性
                 }
             });
             
             // 存储实体引用，用于后续更新
             this.trackPolylines[trackId] = polylineEntity;
             
             // 立即更新轨迹线
             this.updateTrackPolylinePositions(trackId, trackData);
             
             // 添加起点标签
             if (trackData.points.length > 0) {
                 const startPoint = trackData.points[0];
                 
                 // 验证起点坐标的有效性
                 if (startPoint && 
                     typeof startPoint.longitude === 'number' && !isNaN(startPoint.longitude) &&
                     typeof startPoint.latitude === 'number' && !isNaN(startPoint.latitude) &&
                     typeof startPoint.altitude === 'number' && !isNaN(startPoint.altitude) &&
                     startPoint.longitude >= -180 && startPoint.longitude <= 180 &&
                     startPoint.latitude >= -90 && startPoint.latitude <= 90) {
                     
                     const labelText = trackData.aircraft_type ? 
                         `${trackData.aircraft_type}\n${trackData.mission_type || '轨迹'}` : 
                         `轨迹${this.flightTracks.length}`;
                     
                     try {
                         this.viewer.entities.add({
                             position: Cesium.Cartesian3.fromDegrees(startPoint.longitude, startPoint.latitude, startPoint.altitude),
                             label: {
                                 text: labelText,
                                 font: '12pt sans-serif',
                                 style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                                 outlineWidth: 2,
                                 verticalOrigin: Cesium.VerticalOrigin.TOP,
                                 pixelOffset: new Cesium.Cartesian2(0, 10),
                                 fillColor: trackData.color,
                                 outlineColor: Cesium.Color.BLACK
                             }
                         });
                     } catch (e) {
                         console.warn('创建起点标签失败:', startPoint, e);
                     }
                 }
             }
         },
        
        updateTrackPolyline(trackId, trackData) {
            // 更新轨迹线位置
            this.updateTrackPolylinePositions(trackId, trackData);
        },
        
                 updateTrackPolylinePositions(trackId, trackData) {
             const polylineEntity = this.trackPolylines[trackId];
             if (!polylineEntity) return;
             
             const positions = [];
             const currentTime = Date.now();
             
             trackData.points.forEach(point => {
                 // 验证坐标数据的有效性
                 if (point && 
                     typeof point.longitude === 'number' && !isNaN(point.longitude) &&
                     typeof point.latitude === 'number' && !isNaN(point.latitude) &&
                     typeof point.altitude === 'number' && !isNaN(point.altitude) &&
                     point.longitude >= -180 && point.longitude <= 180 &&
                     point.latitude >= -90 && point.latitude <= 90) {
                     try {
                         positions.push(Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.altitude));
                     } catch (e) {
                         console.warn('无效的坐标点:', point, e);
                     }
                 }
             });
             
             // 批量更新位置，减少渲染次数
             if (positions.length >= 2) {
                 polylineEntity.polyline.positions = positions;
                 
                 // 更新轨迹线颜色：飞过的轨迹用青色，未来的轨迹用原色
                 this.updateTrackPolylineColor(trackId, trackData, currentTime);
             }
         },
         
         updateTrackPolylineColor(trackId, trackData, currentTime) {
             const polylineEntity = this.trackPolylines[trackId];
             if (!polylineEntity || !trackData.points || trackData.points.length === 0) return;
             
             // 如果轨迹还没有开始，全部用原色
             if (currentTime < trackData.startTime) {
                 polylineEntity.polyline.material = trackData.color.withAlpha(0.8);
                 return;
             }
             
             // 如果轨迹已经结束，全部用青色
             const lastPointTime = trackData.startTime + trackData.points[trackData.points.length - 1].timestamp * 1000;
             if (currentTime >= lastPointTime) {
                 polylineEntity.polyline.material = Cesium.Color.CYAN.withAlpha(0.8);
                 return;
             }
             
             // 找到当前时间对应的轨迹点索引
             let currentPointIndex = 0;
             for (let i = 0; i < trackData.points.length; i++) {
                 const pointTime = trackData.startTime + trackData.points[i].timestamp * 1000;
                 if (currentTime >= pointTime) {
                     currentPointIndex = i;
                 } else {
                     break;
                 }
             }
             
             // 计算已飞过的轨迹比例
             const flownRatio = currentPointIndex / (trackData.points.length - 1);
             
             // 如果大部分轨迹已经飞过（超过50%），用青色；否则用原色
             if (flownRatio > 0.5) {
                 polylineEntity.polyline.material = Cesium.Color.CYAN.withAlpha(0.8);
             } else {
                 polylineEntity.polyline.material = trackData.color.withAlpha(0.8);
             }
         },
         
         // 创建分段颜色的轨迹线（更精确的方法）
         createSegmentedTrackPolyline(trackId, trackData, currentTime) {
             // 移除现有的轨迹线
             const existingPolyline = this.trackPolylines[trackId];
             if (existingPolyline) {
                 this.viewer.entities.remove(existingPolyline);
             }
             
             // 找到当前时间对应的轨迹点索引
             let currentPointIndex = 0;
             for (let i = 0; i < trackData.points.length; i++) {
                 const pointTime = trackData.startTime + trackData.points[i].timestamp * 1000;
                 if (currentTime >= pointTime) {
                     currentPointIndex = i;
                 } else {
                     break;
                 }
             }
             
             // 创建飞过的轨迹段（青色）
             if (currentPointIndex > 0) {
                 const flownPositions = [];
                 for (let i = 0; i <= currentPointIndex; i++) {
                     const point = trackData.points[i];
                     if (point && 
                         typeof point.longitude === 'number' && !isNaN(point.longitude) &&
                         typeof point.latitude === 'number' && !isNaN(point.latitude) &&
                         typeof point.altitude === 'number' && !isNaN(point.altitude)) {
                         try {
                             flownPositions.push(Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.altitude));
                         } catch (e) {
                             console.warn('无效的坐标点:', point, e);
                         }
                     }
                 }
                 
                 if (flownPositions.length >= 2) {
                     const flownPolyline = this.viewer.entities.add({
                         polyline: {
                             positions: flownPositions,
                             width: 3,
                             material: Cesium.Color.CYAN.withAlpha(0.8),
                             clampToGround: false,
                             show: this.showHistoricalTracks
                         }
                     });
                     
                     // 存储飞过的轨迹段引用
                     if (!this.trackPolylines[trackId + '_flown']) {
                         this.trackPolylines[trackId + '_flown'] = flownPolyline;
                     }
                 }
             }
             
             // 创建未来的轨迹段（原色）
             if (currentPointIndex < trackData.points.length - 1) {
                 const futurePositions = [];
                 for (let i = currentPointIndex; i < trackData.points.length; i++) {
                     const point = trackData.points[i];
                     if (point && 
                         typeof point.longitude === 'number' && !isNaN(point.longitude) &&
                         typeof point.latitude === 'number' && !isNaN(point.latitude) &&
                         typeof point.altitude === 'number' && !isNaN(point.altitude)) {
                         try {
                             futurePositions.push(Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.altitude));
                         } catch (e) {
                             console.warn('无效的坐标点:', point, e);
                         }
                     }
                 }
                 
                 if (futurePositions.length >= 2) {
                     const futurePolyline = this.viewer.entities.add({
                         polyline: {
                             positions: futurePositions,
                             width: 3,
                             material: trackData.color.withAlpha(0.8),
                             clampToGround: false,
                             show: this.showHistoricalTracks
                         }
                     });
                     
                     // 存储未来的轨迹段引用
                     this.trackPolylines[trackId] = futurePolyline;
                 }
             }
         },
        
                 createAircraftEntity(trackId, trackData) {
             // 创建飞机实体（飞行器图标）- 使用静态位置，避免频繁的CallbackProperty调用
             const aircraftEntity = this.viewer.entities.add({
                 position: Cesium.Cartesian3.fromDegrees(0, 0, 0), // 初始位置
                 billboard: {
                     image: this.createColoredDroneIcon(trackData.color),
                     width: 48,
                     height: 48,
                     eyeOffset: new Cesium.Cartesian3(0, 0, -100)
                 },
                                 label: {
                     text: trackData.aircraft_type || `飞机${this.flightTracks.length}`,
                     font: '12pt sans-serif',
                     style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                     outlineWidth: 2,
                     verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                     pixelOffset: new Cesium.Cartesian2(0, -30),
                     fillColor: trackData.color,
                     outlineColor: Cesium.Color.BLACK
                 }
            });
            
            this.aircraftEntities[trackId] = aircraftEntity;
            
            // 立即更新飞机位置
            this.updateAircraftPosition(trackId, trackData);
        },
        
        updateAircraftPosition(trackId, trackData) {
            const aircraftEntity = this.aircraftEntities[trackId];
            if (!aircraftEntity) return;
            
            const track = this.flightTracks.find(t => t.id === trackId);
            if (!track || !track.points || track.points.length === 0) return;
            
            // 找到当前时间对应的点
            const currentTime = Date.now();
            const currentPoint = this.getCurrentPoint(track, currentTime);
            
            if (currentPoint && 
                typeof currentPoint.longitude === 'number' && !isNaN(currentPoint.longitude) &&
                typeof currentPoint.latitude === 'number' && !isNaN(currentPoint.latitude) &&
                typeof currentPoint.altitude === 'number' && !isNaN(currentPoint.altitude) &&
                currentPoint.longitude >= -180 && currentPoint.longitude <= 180 &&
                currentPoint.latitude >= -90 && currentPoint.latitude <= 90) {
                try {
                    // 更新飞机位置
                    aircraftEntity.position = Cesium.Cartesian3.fromDegrees(
                        currentPoint.longitude, 
                        currentPoint.latitude, 
                        currentPoint.altitude
                    );
                    
                    // 更新标签文本（包含电池信息）
                    if (currentPoint.battery !== undefined) {
                        const batteryPercent = Math.round(currentPoint.battery * 100);
                        aircraftEntity.label.text = `${track.aircraft_type || '飞机'}\n电池: ${batteryPercent}%`;
                    } else {
                        aircraftEntity.label.text = track.aircraft_type || `飞机${this.flightTracks.length}`;
                    }
                } catch (e) {
                    console.warn('更新飞机位置失败:', currentPoint, e);
                }
            }
        },
        
                 getCurrentPoint(track, currentTime) {
             if (!track.points || track.points.length === 0) return null;
             
             // 验证轨迹点数据的有效性
             const validPoints = track.points.filter(point => 
                 point && 
                 typeof point.longitude === 'number' && !isNaN(point.longitude) &&
                 typeof point.latitude === 'number' && !isNaN(point.latitude) &&
                 typeof point.altitude === 'number' && !isNaN(point.altitude) &&
                 point.longitude >= -180 && point.longitude <= 180 &&
                 point.latitude >= -90 && point.latitude <= 90
             );
             
             if (validPoints.length === 0) return null;
             
             const trackTime = track.startTime + validPoints[validPoints.length - 1].timestamp * 1000;
             
             // 如果轨迹还没开始，返回起点
             if (currentTime < track.startTime) {
                 return validPoints[0];
             }
             
             // 如果轨迹已经结束，返回终点
             if (currentTime > trackTime) {
                 return validPoints[validPoints.length - 1];
             }
             
             // 找到当前时间对应的点
             for (let i = 0; i < validPoints.length - 1; i++) {
                 const point1 = validPoints[i];
                 const point2 = validPoints[i + 1];
                 const time1 = track.startTime + point1.timestamp * 1000;
                 const time2 = track.startTime + point2.timestamp * 1000;
                 
                 if (currentTime >= time1 && currentTime <= time2) {
                     // 线性插值
                     const ratio = (currentTime - time1) / (time2 - time1);
                     return {
                         longitude: point1.longitude + (point2.longitude - point1.longitude) * ratio,
                         latitude: point1.latitude + (point2.latitude - point1.latitude) * ratio,
                         altitude: point1.altitude + (point2.altitude - point1.altitude) * ratio,
                         battery: point1.battery // 保持电池信息
                     };
                 }
             }
             
             return validPoints[0];
         },
        
        addPointsToTrack(trackId, newPoints) {
            // 这里可以添加新点到轨迹的逻辑
            // 由于使用了CallbackProperty，轨迹线会自动更新
        },
        
                 clearAllTracks() {
             if (confirm('确定要清除所有轨迹吗？')) {
                 // 停止播放
                 this.stopPlayback();
                 
                 // 清除所有轨迹实体
                 Object.values(this.trackPolylines).forEach(entity => {
                     if (entity) this.viewer.entities.remove(entity);
                 });
                 Object.values(this.aircraftEntities).forEach(entity => {
                     if (entity) this.viewer.entities.remove(entity);
                 });
                 
                 // 清空数据
                 this.trackPolylines = {};
                 this.aircraftEntities = {};
                 this.flightTracks = [];
                 this.currentTrackIndex = 0;
                 this.flightPath = [];
                 
                 // 重置时间轴
                 this.currentTime = 0;
                 this.totalDuration = 0;
                 this.currentTimePercent = 0;
                 
                 this.status = '已清除所有轨迹';
             }
         },
        
        clearHistoricalTracks() {
            if (confirm('确定要清除历史轨迹吗？飞机位置将保留')) {
                // 清除轨迹线实体，但保留飞机实体
                Object.values(this.trackPolylines).forEach(entity => {
                    if (entity) this.viewer.entities.remove(entity);
                });
                
                // 清空轨迹线引用
                this.trackPolylines = {};
                
                // 清空轨迹数据中的历史点，只保留最新的点
                this.flightTracks.forEach(track => {
                    if (track.points && track.points.length > 0) {
                        // 只保留最后一个点
                        track.points = [track.points[track.points.length - 1]];
                    }
                });
                
                this.status = '已清除历史轨迹，飞机位置保留';
            }
        },
        
        updateRenderDelay() {
            // 更新渲染间隔
            if (this.renderInterval) {
                clearInterval(this.renderInterval);
            }
            if (this.enableRealTimeUpdate) {
                this.renderInterval = setInterval(() => {
                    this.renderNewPoints();
                }, this.renderDelay);
            }
        },
        
        toggleRealTimeUpdate() {
            if (this.enableRealTimeUpdate) {
                // 启用实时更新
                this.updateRenderDelay();
            } else {
                // 禁用实时更新
                if (this.renderInterval) {
                    clearInterval(this.renderInterval);
                    this.renderInterval = null;
                }
            }
        },
        
        toggleHistoricalTracks() {
            // 更新所有轨迹线的可见性
            Object.keys(this.trackPolylines).forEach(trackId => {
                const polylineEntity = this.trackPolylines[trackId];
                if (polylineEntity) {
                    polylineEntity.show = this.showHistoricalTracks;
                }
            });
            
            // 更新状态信息
            if (this.showHistoricalTracks) {
                this.status = '已显示历史轨迹';
            } else {
                this.status = '已隐藏历史轨迹';
            }
        },
        
                 // 创建飞行器图标
         createColoredDroneIcon(color) {
             // 使用指定的飞行器图片
             return 'http://211.159.175.30:8011/uamtest/image/fly/flane.png';
         },
        
        // 设置当前轨迹（实时模式下简化）
                 setCurrentTrack(trackIndex) {
             this.currentTrackIndex = trackIndex;
             if (this.flightTracks[trackIndex]) {
                 this.flightPath = this.flightTracks[trackIndex].points;
                 this.status = `已选择轨迹${trackIndex + 1}，实时模式运行中`;
                 
                 // 初始化时间轴
                 this.initializeTimeline();
             }
         },
         
         // ================= 时间轴控制 =================
         initializeTimeline() {
             if (!this.hasValidTrack) return;
             
             const track = this.flightTracks[this.currentTrackIndex];
             if (track.points && track.points.length > 0) {
                 // 计算总时长
                 const lastPoint = track.points[track.points.length - 1];
                 this.totalDuration = lastPoint.timestamp;
                 this.currentTime = 0;
                 this.currentTimePercent = 0;
                 
                 // 停止当前播放
                 this.stopPlayback();
             }
         },
         
         playPause() {
             if (!this.hasValidTrack) return;
             
             if (this.playing) {
                 this.stopPlayback();
             } else {
                 this.startPlayback();
             }
         },
         
         startPlayback() {
             if (!this.hasValidTrack) return;
             
             this.playing = true;
             this.playbackInterval = setInterval(() => {
                 this.currentTime += this.playbackSpeed * 0.1; // 每0.1秒更新一次
                 
                 if (this.currentTime >= this.totalDuration) {
                     this.currentTime = this.totalDuration;
                     this.stopPlayback();
                 }
                 
                 this.currentTimePercent = (this.currentTime / this.totalDuration) * 100;
                 this.updateAircraftPositionForTime();
             }, 100);
         },
         
         stopPlayback() {
             this.playing = false;
             if (this.playbackInterval) {
                 clearInterval(this.playbackInterval);
                 this.playbackInterval = null;
             }
         },
         
         stepForward() {
             if (!this.hasValidTrack) return;
             
             this.currentTime += 10; // 向前10秒
             if (this.currentTime > this.totalDuration) {
                 this.currentTime = this.totalDuration;
             }
             this.currentTimePercent = (this.currentTime / this.totalDuration) * 100;
             this.updateAircraftPositionForTime();
         },
         
         stepBackward() {
             if (!this.hasValidTrack) return;
             
             this.currentTime -= 10; // 向后10秒
             if (this.currentTime < 0) {
                 this.currentTime = 0;
             }
             this.currentTimePercent = (this.currentTime / this.totalDuration) * 100;
             this.updateAircraftPositionForTime();
         },
         
         resetTime() {
             if (!this.hasValidTrack) return;
             
             this.currentTime = 0;
             this.currentTimePercent = 0;
             this.updateAircraftPositionForTime();
         },
         
         seekToTime() {
             if (!this.hasValidTrack) return;
             
             this.currentTime = (this.currentTimePercent / 100) * this.totalDuration;
             this.updateAircraftPositionForTime();
         },
         
         updatePlaybackSpeed() {
             // 播放速度更新，不需要额外处理
         },
         
         updateAircraftPositionForTime() {
             if (!this.hasValidTrack) return;
             
             const track = this.flightTracks[this.currentTrackIndex];
             const targetTime = track.startTime + this.currentTime * 1000; // 转换为毫秒
             
             // 找到当前时间对应的点
             const currentPoint = this.getCurrentPoint(track, targetTime);
             
             if (currentPoint) {
                 // 更新飞机位置
                 const aircraftEntity = this.aircraftEntities[track.id];
                 if (aircraftEntity) {
                     try {
                         aircraftEntity.position = Cesium.Cartesian3.fromDegrees(
                             currentPoint.longitude,
                             currentPoint.latitude,
                             currentPoint.altitude
                         );
                         
                         // 更新标签
                         if (currentPoint.battery !== undefined) {
                             const batteryPercent = Math.round(currentPoint.battery * 100);
                             aircraftEntity.label.text = `${track.aircraft_type || '飞机'}\n电池: ${batteryPercent}%`;
                         } else {
                             aircraftEntity.label.text = track.aircraft_type || `飞机${this.currentTrackIndex + 1}`;
                         }
                     } catch (e) {
                         console.warn('更新飞机位置失败:', currentPoint, e);
                     }
                 }
                 
                 // 更新轨迹线颜色（时间轴模式）
                 this.updateTrackPolylineColor(track.id, track, targetTime);
                 
                 // 更新位置信息显示
                 const trackName = track.aircraft_type || `轨迹${this.currentTrackIndex + 1}`;
                 const batteryInfo = currentPoint.battery !== undefined ? ` - 电池: ${Math.round(currentPoint.battery * 100)}%` : '';
                 
                 this.posText = `${trackName}: ${currentPoint.longitude.toFixed(4)}, ${currentPoint.latitude.toFixed(4)}`;
                 this.segText = `时间轴位置 - 高度: ${currentPoint.altitude}m${batteryInfo}`;
             }
         },

                 onTick() {
             // 只有在非播放模式下才显示实时位置
             if (!this.playing && this.flightPath && this.flightPath.length > 0) {
                 const currentTime = Date.now();
                 const currentPoint = this.getCurrentPoint(this.flightTracks[this.currentTrackIndex], currentTime);
                 
                 if (currentPoint) {
                     const track = this.flightTracks[this.currentTrackIndex];
                     const trackName = track.aircraft_type || `轨迹${this.currentTrackIndex + 1}`;
                     const batteryInfo = currentPoint.battery !== undefined ? ` - 电池: ${Math.round(currentPoint.battery * 100)}%` : '';
                     
                     this.posText = `${trackName}: ${currentPoint.longitude.toFixed(4)}, ${currentPoint.latitude.toFixed(4)}`;
                     this.segText = `实时位置 - 高度: ${currentPoint.altitude}m${batteryInfo}`;
                 }
             }
         },

         
         // ================= 多边形绘制 =================
         startDrawPolygon() {
             if (this.drawing) return;
             this.drawing = true;
             this.drawPositions = [];
             if (this.drawPolygonEntity) { this.viewer.entities.remove(this.drawPolygonEntity); this.drawPolygonEntity = null; }
             if (this.drawOutlineEntity) { this.viewer.entities.remove(this.drawOutlineEntity); this.drawOutlineEntity = null; }
             if (this.drawHandler) { this.drawHandler.destroy(); this.drawHandler = null; }
             this.drawHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.canvas);

             // 左键点击：落点
             this.drawHandler.setInputAction((movement) => {
                 const cart = this.getCartesianFromMouse(movement.position);
                 if (!cart) return;
                 this.drawPositions.push(cart);
                 this.ensureDynamicPolygon();
             }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

             // 右键点击：完成绘制
             this.drawHandler.setInputAction((movement) => {
                 if (this.drawing && this.drawPositions.length >= 3) {
                     this.finishDrawPolygon();
                 }
             }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);

             // 移动：动态预览末点
             this.drawHandler.setInputAction((movement) => {
                 if (!this.drawing || this.drawPositions.length === 0) return;
                 const cart = this.getCartesianFromMouse(movement.endPosition);
                 if (!cart) return;
                 const temp = this.drawPositions.slice();
                 temp.push(cart);
                 this.updateDynamicPolygon(temp);
             }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
         },
         finishDrawPolygon() {
             if (!this.drawing) return;
             this.drawing = false;
             if (this.drawHandler) { this.drawHandler.destroy(); this.drawHandler = null; }
             // 最终以 drawPositions 生成实体
             if (this.drawPositions.length < 3) return;
             this.updateDynamicPolygon(this.drawPositions, true);
         },
         clearDrawPolygon() {
             this.drawing = false;
             this.drawPositions = [];
             this.polygonGeoJSON = null; // 清除GeoJSON
             
             // 清除所有记录及其对应的实体
             for (const record of this.geojsonRecords) {
                 if (record.polygonEntity) {
                     this.viewer.entities.remove(record.polygonEntity);
                 }
                 if (record.outlineEntity) {
                     this.viewer.entities.remove(record.outlineEntity);
                 }
             }
             this.geojsonRecords = []; // 清空记录数组
             
             if (this.drawHandler) { this.drawHandler.destroy(); this.drawHandler = null; }
             if (this.drawPolygonEntity) { this.viewer.entities.remove(this.drawPolygonEntity); this.drawPolygonEntity = null; }
             if (this.drawOutlineEntity) { this.viewer.entities.remove(this.drawOutlineEntity); this.drawOutlineEntity = null; }
         },
         getCartesianFromMouse(winPos) {
             if (!winPos) return null;
             const scene = this.viewer.scene;
             let cartesian = null;
             if (scene.pickPositionSupported) {
                 cartesian = scene.pickPosition(winPos);
             }
             if (!Cesium.defined(cartesian)) {
                 const ray = this.viewer.camera.getPickRay(winPos);
                 cartesian = scene.globe.pick(ray, scene);
             }
             return cartesian || null;
         },
         ensureDynamicPolygon() {
             if (this.drawPolygonEntity) return;
             const hierarchyCb = new Cesium.CallbackProperty(() => {
                 const list = (this._previewPositions || this.drawPositions);
                 if (!list || list.length < 3) return null;
                 return new Cesium.PolygonHierarchy(list);
             }, false);
             this.drawPolygonEntity = this.viewer.entities.add({
                 polygon: {
                     hierarchy: hierarchyCb,
                     material: Cesium.Color.fromBytes(255, 165, 0, 100), // 橙色半透明
                     perPositionHeight: true,
                     outline: false
                 }
             });
             this.drawOutlineEntity = this.viewer.entities.add({
                 polyline: {
                     positions: new Cesium.CallbackProperty(() => {
                         const list = (this._previewPositions || this.drawPositions);
                         if (!list || list.length < 2) return [];
                         return list.concat(list[0]); // 闭合预览
                     }, false),
                     width: 2,
                     material: Cesium.Color.ORANGE
                 }
             });
         },
         updateDynamicPolygon(positions, finalize = false) {
             this._previewPositions = positions;
             if (finalize) {
                 // 清除预览实体
                 if (this.drawPolygonEntity) { this.viewer.entities.remove(this.drawPolygonEntity); this.drawPolygonEntity = null; }
                 if (this.drawOutlineEntity) { this.viewer.entities.remove(this.drawOutlineEntity); this.drawOutlineEntity = null; }
                 this._previewPositions = null;
                 
                 // 生成GeoJSON（会自动创建实体并添加到记录中）
                 this.generateGeoJSON(positions);
             }
         },
         
         // 生成GeoJSON
         generateGeoJSON(positions) {
             if (!positions || positions.length < 3) {
                 this.polygonGeoJSON = null;
                 return;
             }
             
             // 将Cartesian3坐标转换为经纬度
             const coordinates = [];
             for (const pos of positions) {
                 const cartographic = Cesium.Cartographic.fromCartesian(pos);
                 const longitude = Cesium.Math.toDegrees(cartographic.longitude);
                 const latitude = Cesium.Math.toDegrees(cartographic.latitude);
                 coordinates.push([longitude, latitude]);
             }
             
             // 闭合多边形（首尾相连）
             if (coordinates.length > 0) {
                 coordinates.push(coordinates[0]);
             }
             
             const geojson = {
                 type: "Feature",
                 properties: {
                     name: "绘制多边形",
                     timestamp: new Date().toISOString()
                 },
                 geometry: {
                     type: "Polygon",
                     coordinates: [coordinates]
                 }
             };
             
             this.polygonGeoJSON = JSON.stringify(geojson, null, 2);
             
             // 添加到记录表格
             this.geojsonRecords.push({
                 timestamp: new Date(),
                 pointCount: positions.length,
                 geojson: geojson,
                 geojsonString: this.polygonGeoJSON,
                 polygonEntity: null, // 将在下面设置
                 outlineEntity: null  // 将在下面设置
             });
             
             // 获取刚添加的记录索引
             const recordIndex = this.geojsonRecords.length - 1;
             
             // 创建多边形实体并保存引用
             const polygonEntity = this.viewer.entities.add({
                 polygon: {
                     hierarchy: new Cesium.PolygonHierarchy(positions),
                     material: Cesium.Color.fromBytes(255, 165, 0, 100),
                     perPositionHeight: true
                 }
             });
             
             const outlineEntity = this.viewer.entities.add({
                 polyline: {
                     positions: positions.concat(positions[0]),
                     width: 2,
                     material: Cesium.Color.ORANGE
                 }
             });
             
             // 保存实体引用到记录中
             this.geojsonRecords[recordIndex].polygonEntity = polygonEntity;
             this.geojsonRecords[recordIndex].outlineEntity = outlineEntity;
         },
         
         // 复制GeoJSON到剪贴板
         copyGeoJSON() {
             if (!this.polygonGeoJSON) return;
             
             navigator.clipboard.writeText(this.polygonGeoJSON).then(() => {
                 // 临时显示复制成功提示
                 const btn = event.target;
                 const originalText = btn.textContent;
                 btn.textContent = '已复制';
                 btn.style.background = '#4CAF50';
                 setTimeout(() => {
                     btn.textContent = originalText;
                     btn.style.background = '#2196F3';
                 }, 1000);
             }).catch(err => {
                 console.error('复制失败:', err);
                 // 降级方案：使用传统方法
                 const textArea = document.createElement('textarea');
                 textArea.value = this.polygonGeoJSON;
                 document.body.appendChild(textArea);
                 textArea.select();
                 document.execCommand('copy');
                 document.body.removeChild(textArea);
                 
                 const btn = event.target;
                 const originalText = btn.textContent;
                 btn.textContent = '已复制';
                 btn.style.background = '#4CAF50';
                 setTimeout(() => {
                     btn.textContent = originalText;
                     btn.style.background = '#2196F3';
                 }, 1000);
             });
         },
         
         // 格式化时间显示
         formatTime(date) {
             if (!date) return '-';
             const d = new Date(date);
             return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}:${d.getSeconds().toString().padStart(2, '0')}`;
         },
         
         // 复制记录中的GeoJSON
         copyRecordGeoJSON(index) {
             const record = this.geojsonRecords[index];
             if (!record) return;
             
             navigator.clipboard.writeText(record.geojsonString).then(() => {
                 // 临时显示复制成功提示
                 const btn = event.target;
                 const originalText = btn.textContent;
                 btn.textContent = '已复制';
                 btn.style.background = '#4CAF50';
                 setTimeout(() => {
                     btn.textContent = originalText;
                     btn.style.background = '#2196F3';
                 }, 1000);
             }).catch(err => {
                 console.error('复制失败:', err);
                 // 降级方案
                 const textArea = document.createElement('textarea');
                 textArea.value = record.geojsonString;
                 document.body.appendChild(textArea);
                 textArea.select();
                 document.execCommand('copy');
                 document.body.removeChild(textArea);
                 
                 const btn = event.target;
                 const originalText = btn.textContent;
                 btn.textContent = '已复制';
                 btn.style.background = '#4CAF50';
                 setTimeout(() => {
                     btn.textContent = originalText;
                     btn.style.background = '#2196F3';
                 }, 1000);
             });
         },
         
         // 查看记录中的完整GeoJSON
         viewRecordGeoJSON(index) {
             const record = this.geojsonRecords[index];
             if (!record) return;
             
             // 显示在弹窗中
             alert(`GeoJSON记录 ${index + 1}:\n\n${record.geojsonString}`);
         },
         
         // 删除记录
         deleteRecord(index) {
             if (confirm(`确定要删除第 ${index + 1} 条记录吗？`)) {
                 const record = this.geojsonRecords[index];
                 if (record) {
                     // 删除对应的多边形实体
                     if (record.polygonEntity) {
                         this.viewer.entities.remove(record.polygonEntity);
                     }
                     if (record.outlineEntity) {
                         this.viewer.entities.remove(record.outlineEntity);
                     }
                 }
                 // 从数组中删除记录
                 this.geojsonRecords.splice(index, 1);
             }
         }
    }
}).mount('#app');
</script>
</body>
</html>
        